package org.dromara.system.kernel.channel;

import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.encrypt.utils.EncryptUtils;
import org.dromara.system.domain.*;
import org.dromara.system.domain.bo.OrderRelateBo;
import org.dromara.system.domain.bo.PayGroupTypeQueryBo;
import org.dromara.system.domain.bo.aparams.AInnerPayBo;
import org.dromara.system.domain.bo.aparams.AOrderBo;
import org.dromara.system.domain.bo.cparams.OrderUpdateBo;
import org.dromara.system.kernel.OrderInnerPayKernel;
import org.dromara.system.kernel.OrderUpdateKernel;
import org.dromara.system.service.IPayAccountService;
import org.dromara.system.service.IPayOrderLogService;
import org.dromara.system.service.IPayOrderService;
import org.dromara.system.utils.RequestUtils;
import org.dromara.system.utils.SendEmailUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.CompletableFuture;

@RequiredArgsConstructor
@Service
public class AdyenKernel {

    private final IPayOrderLogService orderLogService;
    private final IPayOrderService orderService;
    private final OrderInnerPayKernel orderInnerPayKernel;
    private final OrderUpdateKernel orderUpdateKernel;
    private final IPayAccountService accountService;
    @Value("${gatewayDomain}")
    public String gatewayDomain;

    /**
     * 生成内嵌支付sessionId
     *
     * @param order 订单信息
     */
    public R<PayOrder> generateSessionId(PayOrder order, PayOrderLog log) {
        try {
            String sessionId = EncryptUtils.encryptBySm4(order.getOrderId() + "", EncryptUtils.PRIVATE_KEY);

            //写到订单对象里，返出去使用
            order.setSessionId(sessionId);

            //更新订单
            PayOrder up = new PayOrder();
            up.setOrderId(order.getOrderId());
            up.setSessionId(sessionId);
            orderService.updatePayOrderAsync(up);

            //记录更新日志
            orderLogService.saveLog(log != null ? log.getLogId() : null, order.getOrderId(), order.getOrderNo(), order.getOrderAid(), "更新B端订单信息", JSONObject.toJSONString(up), "success", 10L);

            return R.ok(order);

        } catch (Exception e) {
            return R.fail("Request link fail", order);
        }
    }

    /**
     * 生成内嵌支付sessionId
     *
     * @param relateBo 订单关联信息
     */
    public R<PayOrder> requestPayLink(OrderRelateBo relateBo) {
        PayOrder order = relateBo.getOrder();
        PayType payType = relateBo.getPayType();
        PayAccount account = relateBo.getAccount();
        PayGroupAccount groupAccount = relateBo.getGroupAccount();
        PayOrderLog log = relateBo.getLog();

        //官方内嵌
        if (Objects.equals(groupAccount.getPayMethod(), 1)) {
            //v1版本
            if (!Objects.equals(relateBo.getBo().getVersion(), "v2")) {
                return generateSessionId(order, log);
            }

            try {
                //构建直连内嵌链接
                String innerLink = orderInnerPayKernel.buildDirectInnerLink(order, payType, account);
                //写到订单对象里，返出去使用
                order.setLink(innerLink);

                //更新订单
                PayOrder up = new PayOrder();
                up.setOrderId(order.getOrderId());
                up.setLink(innerLink);
                orderService.updatePayOrderAsync(up);

                //记录更新日志
                orderLogService.saveLog(log != null ? log.getLogId() : null, order.getOrderId(), order.getOrderNo(), order.getOrderAid(), "更新B端订单信息", JSONObject.toJSONString(up), "success", 10L);

                return R.ok(order);

            } catch (Exception e) {
                return R.fail("Request link fail", order);
            }
        }

        //cash支付
        if (Objects.equals(groupAccount.getPayMethod(), 2)) {
            //调用adyen内嵌支付接口，获取返回链接
            R<?> r = adyenPay(order, null, log);
            if (R.isError(r)) {
                return R.fail(r.getMsg(), order);
            }

            try {
                JSONObject data = (JSONObject) r.getData();
                if (data.containsKey("return_url")) {
                    String returnUrl = data.getString("return_url");
                    //写到订单对象里，返出去使用
                    order.setLink(returnUrl);

                    //更新订单
                    PayOrder up = new PayOrder();
                    up.setOrderId(order.getOrderId());
                    up.setLink(returnUrl);
                    orderService.updatePayOrderAsync(up);

                    //记录更新日志
                    orderLogService.saveLog(log != null ? log.getLogId() : null, order.getOrderId(), order.getOrderNo(), order.getOrderAid(), "更新B端订单信息", JSONObject.toJSONString(up), "success", 10L);

                    return R.ok(order);
                }
            } catch (Exception ignore) {
            }

            return R.fail("Request link fail", order);
        }

        //不支持的支付方式
        return R.fail("Not supported payment method", order);
    }


    /**
     * adyen内嵌支付
     *
     * @param order      订单
     * @param innerPayBo 内嵌支付参数
     * @param log        订单日志
     */
    public R<?> adyenPay(PayOrder order, AInnerPayBo innerPayBo, PayOrderLog log) {

        //收款账号
        PayAccount account = accountService.selectById(order.getAccountId());
        if (account == null) {
            return R.fail("Account is invalid");
        }

        //获取账号配置
        JSONObject config = account.getAccountConfig();

        String payApi = config.getString("payApi");
        String secretKey = config.getString("secretKey");
        if (StringUtils.isEmpty(payApi) || StringUtils.isEmpty(secretKey)) {
            return R.fail("Account config is invalid");
        }

        //订单地址信息
        AOrderBo.Address bill = JSONObject.parseObject(order.getCustomer(), AOrderBo.Address.class);

        //组装接口参数
        JSONObject params = new JSONObject();
        params.put("orderid", order.getOrderId() + "_" + (System.currentTimeMillis() / 1000));
        params.put("amount", order.getUsdMoney());
        params.put("email", bill.getEmail());

        //过期时间
        if (innerPayBo != null) {
            String[] expiry = innerPayBo.getExpiry().replaceAll(" ", "").split("/");
            params.put("cardnumber", innerPayBo.getNumber());
            params.put("month", expiry[0]);
            params.put("year", expiry[1]);
            params.put("cvv", innerPayBo.getCvc());

            params.put("cardholder", bill.getFirstName() + " " + bill.getLastName());
            params.put("state", bill.getState());
            params.put("city", bill.getCity());
            params.put("address1", bill.getAddress1());
            params.put("address2", bill.getAddress2());
            params.put("postalcode", bill.getPostcode());
        } else {
            params.put("paytype", "cashapp");
        }

        //调用adyen内嵌支付接口
        long time = System.currentTimeMillis();
        Map<String, String> headers = new HashMap<>();
        headers.put("x-api-key", secretKey);
        String res = new RequestUtils().doPostJson(account.getDomain() + payApi, params.toJSONString(), headers);

        //记录请求日志
        orderLogService.saveLog(log != null ? log.getLogId() : null, order.getOrderId(), order.getOrderNo(), order.getOrderAid(), "请求支付接口", params.toString(JSONWriter.Feature.WriteMapNullValue), res, System.currentTimeMillis() - time);

        CompletableFuture<R<?>> future = CompletableFuture.supplyAsync(() -> {
            try {
                if (StringUtils.isEmpty(res)) {
                    return R.fail("Request pay null");
                }
                //解析返回参数
                JSONObject result = JSONObject.parseObject(res);

                if (result.getInteger("code") != 200) {
                    return R.fail(result.getString("message"));
                }

                JSONObject data = result.getJSONObject("data");
                if (data == null) {
                    return R.fail("Request pay data null");
                }

                //支付失败
                if (data.getInteger("code") == 400) {
                    return R.fail(data.getString("message"));
                }

                //3D验证
                if (data.getInteger("code") == 201) {
                    return R.ok(new JSONObject().fluentPut("return_url", data.getString("url")));
                }

                //支付成功
                if (data.getInteger("code") == 200) {
                    OrderUpdateBo bo = new OrderUpdateBo(order.getOrderId(), 2, null);
                    return orderUpdateKernel.updateOrder(order, bo, log);
                }

                //状态码无法处理
                return R.fail("pay code cannot be processed. " + data.getString("message"));
            } catch (Exception e) {
                return R.fail("Request pay fail");
            }
        });

        //订单失败处理
        try {
            R<?> r = future.get();
            if (R.isError(r)) {
                OrderUpdateBo bo = new OrderUpdateBo(order.getOrderId(), 3, r.getMsg());
                CompletableFuture.runAsync(() -> orderUpdateKernel.updateOrder(order, bo, log));
            }
            return r;
        } catch (Exception e) {
            return R.fail("Request pay fail!");
        }
    }

}
